package cn.whitecao.agent.plugin.jdbc.handler;

import cn.hutool.core.util.ReflectUtil;
import cn.whitecao.agent.core.handler.PluginAopHandler;
import cn.whitecao.agent.core.model.Span;
import cn.whitecao.agent.core.model.TraceModel;
import cn.whitecao.agent.core.repoter.ReporterProcessor;
import cn.whitecao.agent.core.trace.TraceContext;
import com.mysql.cj.ClientPreparedQuery;
import com.mysql.cj.jdbc.ClientPreparedStatement;

/**
 * @author : BashfulHuang
 **/
public class AopHandler implements PluginAopHandler {
    private final static String MYSQL8_PACKAGE_HEAD="com.mysql.cj";

    private final static String MYSQL5_CLASS_NAME="JDBC42PreparedStatement";


    private Span sqlData;

    /**
     * 前置 8.0 以下com.mysql.jdbc.PreparedStatement
     *
     * @param className  类名
     * @param methodName 方法名
     * @param args       参数列表
     * @param oThis      可以当this用
     */
    @Override
    public void before(String className, String methodName, Object[] args, Object oThis) {
        TraceModel traceModel = TraceContext.get();
        Span dataModel = new Span(Span.DataType.SQL);

        dataModel.setTraceId(traceModel.getTraceId());
        dataModel.setAppName(traceModel.getAppName());
        dataModel.setSequenceNumber(traceModel.incAndGetSequenceNumber());
        // TODO: 2021/3/16 这种方式在插件多的时候不可取.
        //  最好是在在创建span的同时把id压入栈中,在调用结束after中弹出栈,调用链中获取parentSpanId的话直接取栈顶的就行了
        dataModel.setParentSpanId(traceModel.getSpanId());

        if (className.startsWith(MYSQL8_PACKAGE_HEAD)) {
            if (oThis instanceof ClientPreparedStatement) {
                String originalSql = ((ClientPreparedQuery) ((ClientPreparedStatement) oThis).getQuery()).getOriginalSql()
                        .replace("\n", "")
                        .replaceAll(" +"," ");
                //后台需要处理一下格式化一下 把所有空格\n去掉然后计算个md5值 由这个md5来判断是否相同
                dataModel.addData("sql", originalSql);
                this.sqlData = dataModel;
            }

        } else if (MYSQL5_CLASS_NAME.equals(oThis.getClass().getName())) {
            String sql = "";
            try {
                sql = "" + ReflectUtil.getFieldValue(oThis, "originalSql");
                sql=sql.replace("\n", "").replaceAll(" +"," ");
            } catch (Exception ignored) {
            }
            dataModel.addData("sql", sql);
            this.sqlData = dataModel;
        }


    }

    /**
     * 后置
     *
     * @param className  类名
     * @param methodName 方法名
     * @param args       参数
     * @param oThis      this
     * @param result     返回值
     * @param throwable  抛出的异常
     */
    @Override
    public void after(String className, String methodName, Object[] args, Object result, Object oThis, Throwable throwable) {
        if (this.sqlData != null) {
            sqlData.calculateExecutionTime();
            if (throwable != null) {
                sqlData.addData("status", "error");
                sqlData.addData("errorMessage", throwable.getMessage());
            } else {
                sqlData.addData("status", "success");
            }
            ReporterProcessor.report(sqlData);
        }
    }
}
